package com.term.service.impl;

import com.term.service.IBaseService;
import com.term.dao.IBaseDao;
import com.term.domain.BaseDomain;
import com.term.domain.utils.DomainPage;
import com.term.domain.common.ConditionSet;
import com.term.domain.enums.BooleanEnums;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Stream;

/**
 * Created by chenGang on 2017/1/12.
 */
public class BaseServiceImpl<T extends BaseDomain> implements IBaseService<T> {

    protected IBaseDao baseDao;

    public BaseServiceImpl() {
    }


    public void setBaseDao(IBaseDao baseDao) {
        this.baseDao = baseDao;
    }
    @Override
    public Optional<T> add(T t) {
        return Optional.ofNullable((T) this.baseDao.save(t));
    }

    @Override
    public Optional<T> getEntityById(Class<T> clazz, long id) {
        return this.getEntityByField(clazz, "id", id);
    }

    @Override
    public List<T> getAllEntities(Class<T> clazz) {
        return this.baseDao.getAllEntities(clazz);
    }

    @Override
    public List<T> getAllEntitiesWithoutDel(Class<T> clazz) {
        return this.getAllEntities(clazz, this.putDelParam(null));
    }

    @Override
    public List<T> getAllEntities(Class<T> clazz, Map<String, Object> fields) {
        return this.baseDao.getEntitiesByFieldList(clazz, fields);
    }

    @Override
    public List<T> getAllEntitiesWithoutDel(Class<T> clazz, Map<String, Object> fields) {
        return this.baseDao.getEntitiesByFieldList(clazz, fields);
    }

    @Override
    public Optional<T> getEntityByField(Class<T> clazz, String fieldName, Object fieldValue) {
        return Optional.ofNullable((T) this.baseDao.getEntityByField(clazz, fieldName, fieldValue));
    }

    @Override
    public Optional<T> getEntityByFieldWithoutDel(Class<T> clazz, String fieldName, Object fieldValue) {
        List<T> allEntities = this.getAllEntities(clazz, this.putDelParam(null, fieldName, fieldValue));
        return CollectionUtils.isEmpty(allEntities) ? Optional.empty() : Optional.ofNullable(allEntities.get(0));
    }

    @Override
    public List<T> getEntitiesByField(Class<T> clazz, String fieldName, Object fieldValue) {
        return this.baseDao.getEntitiesByField(clazz, fieldName, fieldValue);
    }

    @Override
    public List<T> getEntitiesByFieldWithoutDel(Class<T> clazz, String fieldName, Object fieldValue) {
        return this.getAllEntities(clazz, this.putDelParam(null, fieldName, fieldValue));
    }

    @Override
    public List<T> getEntitiesByFieldList(Class<T> clazz, Map<String, Object> fieldNameValueMap) {
        return this.baseDao.getEntitiesByFieldList(clazz, fieldNameValueMap);
    }

    @Override
    public List<T> getEntitiesByFieldListWithoutDel(Class<T> clazz, Map<String, Object> fieldNameValueMap) {
        return this.getAllEntities(clazz, this.putDelParam(fieldNameValueMap));
    }

    @Override
    public DomainPage<T> queryEntitiesPagesByFieldList(Class<T> clazz, Map<String, Object> fieldNameValueMap, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        return this.baseDao.queryEntitiesPagesByFieldList(clazz, fieldNameValueMap, orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> queryEntitiesPagesByFieldListWithoutDel(Class<T> clazz, Map<String, Object> fieldNameValueMap, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        return this.queryEntitiesPagesByFieldList(clazz, this.putDelParam(fieldNameValueMap), orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesPagesByFieldList(Class<T> clazz, Map<String, Object> fieldNameValueMap, int pageIndex, int pageSize) {
        return this.baseDao.getEntitiesPagesByFieldList(clazz, fieldNameValueMap, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesPagesByFieldListWithoutDel(Class<T> clazz, Map<String, Object> fieldNameValueMap, int pageIndex, int pageSize) {
        return this.getEntitiesPagesByFieldList(clazz, this.putDelParam(fieldNameValueMap), pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesPagesByFieldList(Class<T> clazz, Map<String, Object> fieldNameValueMap, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        return this.baseDao.getEntitiesPagesByFieldList(clazz, fieldNameValueMap, orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesPagesByFieldListWithoutDel(Class<T> clazz, Map<String, Object> fieldNameValueMap, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        return this.getEntitiesPagesByFieldList(clazz, this.putDelParam(fieldNameValueMap), orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesByPage(Class<T> clazz, int pageIndex, int pageSize) {
        return this.baseDao.getAllEntitiesByPage(clazz, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesByPageWithoutDel(Class<T> clazz, int pageIndex, int pageSize) {
        return this.getEntitiesPagesByFieldList(clazz, this.putDelParam(null), pageIndex, pageSize);
    }

    @Override
    public Optional<T> delete(T t) {
        return this.add(t);
    }


    @Override
    public Optional<T> update(T t) {
        return this.add(t);
    }

    @Override
    public DomainPage<T> getEntitiesByConditions(Class<T> clazz, ConditionSet conditionSet, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        return this.baseDao.getEntitiesPagesByCondition(clazz, conditionSet, orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public DomainPage<T> getEntitiesByConditionsWithoutDel(Class<T> clazz, ConditionSet conditionSet, String orderBy, IBaseDao.SortBy sortBy, int pageIndex, int pageSize) {
        conditionSet.addCondition("isDeleted", BooleanEnums.F);
        return this.baseDao.getEntitiesPagesByCondition(clazz, conditionSet, orderBy, sortBy, pageIndex, pageSize);
    }

    @Override
    public List<T> getEntitiesByConditions(Class<T> clazz, ConditionSet conditionSet, String orderBy, IBaseDao.SortBy sortBy) {
        return this.baseDao.getEntitiesByCondition(clazz, conditionSet, orderBy, sortBy);
    }

    @Override
    public List<T> getEntitiesByConditionsWithoutDel(Class<T> clazz, ConditionSet conditionSet, String orderBy, IBaseDao.SortBy sortBy) {
        conditionSet.addCondition("isDeleted", BooleanEnums.F);
        return this.baseDao.getEntitiesByCondition(clazz, conditionSet, orderBy, sortBy);
    }

    @Override
    public Optional<T> getEntityByConditions(Class<T> clazz, ConditionSet conditionSet) {
        List list = this.baseDao.getEntitiesByCondition(clazz, conditionSet);
        return CollectionUtils.isEmpty(list)?Optional.empty():Optional.ofNullable((T) list.get(0));
    }


    /**
     * 把isDeleted参数加入到查询map中
     * @param map
     */
    private Map<String, Object> putDelParam(Map<String, Object> map){
        return this.putDelParam(map, null, null);
    }

    /**
     * 把isDeleted参数加入到查询map中
     * @param map
     */
    private Map<String, Object> putDelParam(Map<String, Object> map, String key, Object value){
        if (map == null) map = new HashMap<>();
        map.put("isDeleted", BooleanEnums.F);
        if (key != null) map.put(key, value);
        return map;
    }

    /**
     * 判断该实体中是否有isDeleted字段
     * @param clazz
     * @return
     */
    private Boolean checkDel(Class<T> clazz) {
        Field[] fieldsWithRoot = this.getAllFieldsWithRoot(clazz);
        return Stream.of(fieldsWithRoot).anyMatch(field -> field.getName().equals("isDeleted"));
    }


    /**
     * 取得该实体中所有字段，包括父类的
     * @param clazz
     * @return
     */
    private Field[] getAllFieldsWithRoot(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] dFields = clazz.getDeclaredFields();
        if (null != dFields && dFields.length > 0) fieldList.addAll(Arrays.asList(dFields));

        // 若父类是Object，则直接返回当前Field列表
        Class<?> superClass = clazz.getSuperclass();
        if (superClass == Object.class) return dFields;

        // 递归查询父类的field列表
        Field[] superFields = getAllFieldsWithRoot(superClass);
        if (null != superFields && superFields.length > 0) {
            Stream.of(superFields).filter(field -> !fieldList.contains(field))
                    .forEach(field -> fieldList.add(field));
        }
        Field[] result = new Field[fieldList.size()];
        fieldList.toArray(result);
        return result;
    }
}
